import React, {
  MutableRefObject,
  useCallback,
  useContext,
  useState,
} from "react";
import { AxiosResponse } from "axios";

import { expandErrorReasonRequired } from "interfaces/errors";
import configAPI from "services/entities/config";
import { NotificationContext } from "context/notification";
import { AppContext } from "context/app";

// @ts-ignore
import InputField from "components/forms/fields/InputField";
import Button from "components/buttons/Button/Button";
import TooltipWrapper from "components/TooltipWrapper";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import PremiumFeatureMessage from "components/PremiumFeatureMessage";

import {
  IFormDataIdp,
  IFormErrorsIdp,
  isMissingAnyRequiredField,
  validateFormDataIdp,
} from "./helpers";

const baseClass = "end-user-auth-section";

export interface IEndUserAuthSectionProps {
  setDirty: (dirty: boolean) => void;
  formData: IFormDataIdp;
  setFormData: React.Dispatch<React.SetStateAction<IFormDataIdp>>;
  originalFormData: MutableRefObject<IFormDataIdp>;
  onSubmit: () => void;
}

const EndUserAuthSection = ({
  setDirty,
  formData,
  setFormData,
  originalFormData,
  // Notify parent component of changes, since we're calling our own API
  // rather than using the common config update handler.
  onSubmit: announceChanges,
}: IEndUserAuthSectionProps) => {
  const { config, isPremiumTier } = useContext(AppContext);
  const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled;

  const { renderFlash } = useContext(NotificationContext);
  const [formErrors, setFormErrors] = useState<IFormErrorsIdp | null>(null);

  const enableSaveButton =
    // TODO: it seems like we should allow saving an empty form so that the user can clear their IdP info
    // isEmptyFormData(formData) ||
    !isMissingAnyRequiredField(formData) && !formErrors;

  const onInputChange = useCallback(
    ({ name, value }: { name: keyof IFormDataIdp; value: string }) => {
      const newData = { ...formData, [name]: value?.trim() || "" };
      setFormData(newData);
      setDirty(true);

      const newErrors = validateFormDataIdp(newData);
      if (!newErrors) {
        // don't wait for onBlur to clear form errors if there are no new errors
        setFormErrors(null);
      } else if (formErrors?.[name] && !newErrors[name]) {
        // don't wait for onBlur to update error on this field
        setFormErrors(newErrors);
      } else if (name === "metadata") {
        // FIXME: See comment to InputField component regarding onBlur prop for textarea. For now,
        // this check just always updates form errors whenever metadata field changes because
        // onBlur doesn't currently work for textareas.
        setFormErrors(newErrors);
      }
    },
    [formData, setFormData, formErrors, setDirty]
  );

  const onBlur = useCallback(() => {
    setFormErrors(validateFormDataIdp(formData));
  }, [formData]);

  const onSubmit = useCallback(
    async (e: React.FormEvent<SubmitEvent>) => {
      e.preventDefault();
      const newErrors = validateFormDataIdp(formData);
      if (newErrors) {
        setFormErrors(newErrors);
        return;
      }

      try {
        await configAPI.update({
          mdm: {
            end_user_authentication: {
              ...formData,
            },
          },
        });
        renderFlash("success", "Successfully updated end user authentication!");
        originalFormData.current = { ...formData };
        setDirty(false);
        // Notify parent component of changes, since we're calling our own API
        // rather than using the common config update handler.
        announceChanges();
      } catch (err) {
        const ae = (typeof err === "object" ? err : {}) as AxiosResponse;
        if (ae.status === 422) {
          renderFlash(
            "error",
            `Couldn't update: ${expandErrorReasonRequired(err)}.`
          );
          return;
        }
        renderFlash("error", "Couldn't update. Please try again.");
      }
    },
    [formData, renderFlash, setDirty]
  );

  const renderContent = () => {
    if (!isPremiumTier) {
      return <PremiumFeatureMessage />;
    }

    return (
      <form>
        <p>
          If enabled in{" "}
          <strong>
            Controls &gt; Setup experience &gt; End user authentication
          </strong>
          , end users will be required to authenticate when they first set up
          their host.
        </p>
        <div
          className={`form ${
            gitOpsModeEnabled ? "disabled-by-gitops-mode" : ""
          }`}
        >
          <InputField
            label="Identity provider name"
            onChange={onInputChange}
            onBlur={onBlur}
            name="idp_name"
            value={formData.idp_name}
            parseTarget
            error={formErrors?.idp_name}
            tooltip="A required human friendly name for the identity provider that will provide single sign-on authentication."
          />
          <InputField
            label="Entity ID"
            onChange={onInputChange}
            onBlur={onBlur}
            name="entity_id"
            value={formData.entity_id}
            parseTarget
            error={formErrors?.entity_id}
            tooltip="The required entity ID is a URI that you use to identify Fleet when configuring the identity provider."
          />
          <InputField
            label="Metadata URL"
            helpText={
              <>
                If both <b>Metadata URL</b> and <b>Metadata</b> are specified,{" "}
                <b>Metadata URL</b> will be used.
              </>
            }
            onChange={onInputChange}
            onBlur={onBlur}
            name="metadata_url"
            value={formData.metadata_url}
            parseTarget
            error={formErrors?.metadata_url}
            tooltip="Metadata URL provided by the identity provider."
          />
          <InputField
            label="Metadata"
            type="textarea"
            onChange={onInputChange}
            name="metadata"
            value={formData.metadata}
            parseTarget
            error={formErrors?.metadata}
            tooltip="Metadata XML provided by the identity provider."
          />
        </div>
        <GitOpsModeTooltipWrapper
          tipOffset={-8}
          renderChildren={(disableChildren) => (
            <TooltipWrapper
              tipContent="Complete all required fields to save end user authentication."
              disableTooltip={enableSaveButton || disableChildren}
              underline={false}
            >
              <Button
                disabled={!enableSaveButton || disableChildren}
                onClick={onSubmit}
                className="button-wrap"
              >
                Save
              </Button>
            </TooltipWrapper>
          )}
        />
      </form>
    );
  };

  return <div className={baseClass}>{renderContent()}</div>;
};

export default EndUserAuthSection;
